#include "MarketDataEngine.h"
#include "LogManager.h"
#include "EventEngine.h"
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QDir>

namespace Market {

MarketDataEngine::MarketDataEngine(QObject* parent)
    : QObject(parent)
    , m_pUserApi(nullptr)
    , m_initialized(false)
    , m_isLoggedIn(false)
    , m_requestID(0) {
}

MarketDataEngine::~MarketDataEngine() {
    stop();
}

bool MarketDataEngine::init(const QString& configFile) {
    if (m_initialized) {
        MLOG_WARN("market", "MarketDataEngine already initialized");
        return true;
    }

    if (!loadCtpLibrary(configFile)) {
        MLOG_ERROR("market", "Failed to load CTP library");
        return false;
    }

    m_initialized = true;
    MLOG_DEBUG("market", "MarketDataEngine initialized");
    return true;
}

void MarketDataEngine::start() {
    if (!m_initialized) {
        MLOG_ERROR("market", "MarketDataEngine not initialized");
        return;
    }

    if (m_pUserApi) {
        MLOG_WARN("market", "MarketDataEngine already started");
        return;
    }

    m_pUserApi = m_createApiFunc(m_flowDir.toStdString().c_str(), true, true);
    if (!m_pUserApi) {
        MLOG_ERROR("market", "Failed to create CTP API instance");
        return;
    }

    m_pUserApi->RegisterSpi(this);
    m_pUserApi->RegisterFront(const_cast<char*>(m_frontAddr.toStdString().c_str()));
    m_pUserApi->Init();
    MLOG_TRACE("market", "MarketDataEngine started");
}

void MarketDataEngine::stop() {
    if (m_pUserApi) {
        m_pUserApi->Release();
        m_pUserApi = nullptr;
    }
    m_isLoggedIn = false;
    MLOG_INFO("market", "MarketDataEngine stopped");
}

bool MarketDataEngine::subscribe(const std::string& symbol) {
    if (!m_isLoggedIn || !m_pUserApi) {
        MLOG_ERROR("market", "Not logged in or API not initialized");
        return false;
    }

    char* instruments[] = { const_cast<char*>(symbol.c_str()) };
    int result = m_pUserApi->SubscribeMarketData(instruments, 1);
    
    if (result == 0) {
        m_subscribedSymbols.insert(symbol);
        MLOG_INFO("market", "Subscribed to {}", symbol);
        return true;
    }
    
    MLOG_ERROR("market", "Failed to subscribe {}", symbol);
    return false;
}

bool MarketDataEngine::unsubscribe(const std::string& symbol) {
    if (!m_isLoggedIn || !m_pUserApi) {
        MLOG_ERROR("market", "Not logged in or API not initialized");
        return false;
    }

    char* instruments[] = { const_cast<char*>(symbol.c_str()) };
    int result = m_pUserApi->UnSubscribeMarketData(instruments, 1);
    
    if (result == 0) {
        m_subscribedSymbols.erase(symbol);
        MLOG_DEBUG("market", "Unsubscribed from {}", symbol);
        return true;
    }
    
    MLOG_ERROR("market", "Failed to unsubscribe {}", symbol);
    return false;
}

bool MarketDataEngine::batchSubscribe(const std::vector<std::string>& symbols) {
    if (symbols.empty()) return true;

    std::vector<char*> instruments;
    instruments.reserve(symbols.size());
    for (const auto& symbol : symbols) {
        instruments.push_back(const_cast<char*>(symbol.c_str()));
    }

    int result = m_pUserApi->SubscribeMarketData(instruments.data(), static_cast<int>(instruments.size()));
    if (result == 0) {
        for (const auto& symbol : symbols) {
            m_subscribedSymbols.insert(symbol);
        }
        MLOG_INFO("market", "Batch subscribed {} symbols", symbols.size());
        return true;
    }

    MLOG_ERROR("market", "Failed to batch subscribe symbols");
    return false;
}

bool MarketDataEngine::batchUnsubscribe(const std::vector<std::string>& symbols) {
    if (symbols.empty()) return true;

    std::vector<char*> instruments;
    instruments.reserve(symbols.size());
    for (const auto& symbol : symbols) {
        instruments.push_back(const_cast<char*>(symbol.c_str()));
    }

    int result = m_pUserApi->UnSubscribeMarketData(instruments.data(), static_cast<int>(instruments.size()));
    if (result == 0) {
        for (const auto& symbol : symbols) {
            m_subscribedSymbols.erase(symbol);
        }
        MLOG_INFO("market", "Batch unsubscribed {} symbols", symbols.size());
        return true;
    }

    MLOG_ERROR("market", "Failed to batch unsubscribe symbols");
    return false;
}

std::vector<std::string> MarketDataEngine::getSubscribedSymbols() const {
    return std::vector<std::string>(m_subscribedSymbols.begin(), m_subscribedSymbols.end());
}

void MarketDataEngine::OnFrontConnected() {
    MLOG_TRACE("market", "Front connected");
    emit connected();
    reqUserLogin();
}

void MarketDataEngine::OnFrontDisconnected(int nReason) {
    m_isLoggedIn = false;
    MLOG_ERROR("market", "Front disconnected, reason: {}", nReason);
    emit disconnected(nReason);
}

void MarketDataEngine::OnHeartBeatWarning(int nTimeLapse) {
    MLOG_WARN("market", "Heartbeat warning, time lapse: {}", nTimeLapse);
}

void MarketDataEngine::OnRspUserLogin(CThostFtdcRspUserLoginField *pRspUserLogin,
                                    CThostFtdcRspInfoField *pRspInfo,
                                    int nRequestID,
                                    bool bIsLast) {
    if (!hasError(pRspInfo)) {
        m_isLoggedIn = true;
        MLOG_TRACE("market", "Login successful");
        emit loginSuccess();
    } else {
        m_isLoggedIn = false;
        QString errorMsg = QString::fromLocal8Bit(pRspInfo->ErrorMsg);
        MLOG_ERROR("market", "Login failed: {}", errorMsg.toStdString());
        emit loginFailed(errorMsg);
    }
}

void MarketDataEngine::OnRspUserLogout(CThostFtdcUserLogoutField *pUserLogout,
                                     CThostFtdcRspInfoField *pRspInfo,
                                     int nRequestID,
                                     bool bIsLast) {
    m_isLoggedIn = false;
    if (!hasError(pRspInfo)) {
        MLOG_TRACE("market", "Logout successful");
    } else {
        QString errorMsg = QString::fromLocal8Bit(pRspInfo->ErrorMsg);
        MLOG_ERROR("market", "Logout failed: {}", errorMsg.toStdString());
    }
}

void MarketDataEngine::OnRspError(CThostFtdcRspInfoField *pRspInfo,
                                int nRequestID,
                                bool bIsLast) {
    if (pRspInfo) {
        QString errorMsg = QString::fromLocal8Bit(pRspInfo->ErrorMsg);
        MLOG_ERROR("market", "Error occurred: [{}] {}", pRspInfo->ErrorID, errorMsg.toStdString());
        emit error(pRspInfo->ErrorID, errorMsg);
    }
}

void MarketDataEngine::OnRspSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,
                                        CThostFtdcRspInfoField *pRspInfo,
                                        int nRequestID,
                                        bool bIsLast) {
    if (!hasError(pRspInfo) && pSpecificInstrument) {
        MLOG_DEBUG("market", "Successfully subscribed to {}", pSpecificInstrument->InstrumentID);
    } else if (pRspInfo) {
        QString errorMsg = QString::fromLocal8Bit(pRspInfo->ErrorMsg);
        MLOG_ERROR("market", "Failed to subscribe: {}", errorMsg.toStdString());
    }
}

void MarketDataEngine::OnRspUnSubMarketData(CThostFtdcSpecificInstrumentField *pSpecificInstrument,
                                          CThostFtdcRspInfoField *pRspInfo,
                                          int nRequestID,
                                          bool bIsLast) {
    if (!hasError(pRspInfo) && pSpecificInstrument) {
        MLOG_DEBUG("market", "Successfully unsubscribed from {}", pSpecificInstrument->InstrumentID);
    } else if (pRspInfo) {
        QString errorMsg = QString::fromLocal8Bit(pRspInfo->ErrorMsg);
        MLOG_ERROR("market", "Failed to unsubscribe: {}", errorMsg.toStdString());
    }
}

void MarketDataEngine::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData) {
    if (!pDepthMarketData) return;

    WTSTickStruct tick;
    memset(&tick, 0, sizeof(WTSTickStruct));

    strncpy(tick.code, pDepthMarketData->InstrumentID, sizeof(tick.code) - 1);
    strncpy(tick.exchg, pDepthMarketData->ExchangeID, sizeof(tick.exchg) - 1);

    tick.price = pDepthMarketData->LastPrice;
    tick.open = pDepthMarketData->OpenPrice;
    tick.high = pDepthMarketData->HighestPrice;
    tick.low = pDepthMarketData->LowestPrice;
    tick.pre_close = pDepthMarketData->PreClosePrice;
    tick.settle_price = pDepthMarketData->SettlementPrice;
    tick.upper_limit = pDepthMarketData->UpperLimitPrice;
    tick.lower_limit = pDepthMarketData->LowerLimitPrice;

    tick.total_volume = pDepthMarketData->Volume;
    tick.volume = pDepthMarketData->Volume - m_lastVolumes[pDepthMarketData->InstrumentID];
    m_lastVolumes[pDepthMarketData->InstrumentID] = pDepthMarketData->Volume;
    
    tick.total_turnover = pDepthMarketData->Turnover;
    tick.turn_over = pDepthMarketData->Turnover - m_lastTurnovers[pDepthMarketData->InstrumentID];
    m_lastTurnovers[pDepthMarketData->InstrumentID] = pDepthMarketData->Turnover;
    
    tick.open_interest = pDepthMarketData->OpenInterest;
    tick.diff_interest = pDepthMarketData->OpenInterest - m_lastOpenInterests[pDepthMarketData->InstrumentID];
    m_lastOpenInterests[pDepthMarketData->InstrumentID] = pDepthMarketData->OpenInterest;

    tick.bid_prices[0] = pDepthMarketData->BidPrice1;
    tick.bid_qty[0] = pDepthMarketData->BidVolume1;
    tick.ask_prices[0] = pDepthMarketData->AskPrice1;
    tick.ask_qty[0] = pDepthMarketData->AskVolume1;

    sscanf(pDepthMarketData->ActionDay, "%d", &tick.action_date);
    int hour, minute, second;
    sscanf(pDepthMarketData->UpdateTime, "%d:%d:%d", &hour, &minute, &second);
    tick.action_time = (hour * 10000 + minute * 100 + second) * 1000 + pDepthMarketData->UpdateMillisec;

    Event tickEvent(EventType::TICK);
    tickEvent.data() = tick;
    EVENT_ENGINE->postEvent(std::move(tickEvent));
    emit tickReceived(tick);
}

bool MarketDataEngine::loadCtpLibrary(const QString& configFile) {
    QFile file(configFile);
    if (!file.open(QIODevice::ReadOnly)) {
        MLOG_ERROR("market", "Failed to open config file: {}", configFile.toStdString());
        return false;
    }

    QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
    file.close();

    if (doc.isNull() || !doc.isObject()) {
        MLOG_ERROR("market", "Invalid JSON format in config file");
        return false;
    }

    QJsonObject config = doc.object();
    
    m_brokerID = config["BrokerID"].toString();
    m_userID = config["UserID"].toString();
    m_password = config["Password"].toString();
    m_frontAddr = config["FrontAddr"].toString();
    m_flowDir = config["FlowDir"].toString();
    
    QString ctpLibPath = config["CTPLibPath"].toString();
    QString moduleLib = config["ModuleLib"].toString();
    
    QDir().mkpath(m_flowDir);
    
    QString libPath = QDir(ctpLibPath).absoluteFilePath(moduleLib);
    m_ctpLibrary.setFileName(libPath);
    
    if (!m_ctpLibrary.load()) {
        MLOG_ERROR("market", "Failed to load CTP library: {}", m_ctpLibrary.errorString().toStdString());
        return false;
    }
    
    m_createApiFunc = reinterpret_cast<CreateFtdcMdApiFunc>(m_ctpLibrary.resolve("_ZN15CThostFtdcMdApi15CreateFtdcMdApiEPKcbb"));
    if (!m_createApiFunc) {
        MLOG_ERROR("market", "Failed to resolve CTP API creation function");
        return false;
    }
    
    MLOG_TRACE("market", "CTP library loaded successfully");
    MLOG_INFO("market", "Config Name: {}, Front: {}", config["MarkName"].toString().toLocal8Bit().data(), m_frontAddr.toStdString().c_str(), m_frontAddr.toLocal8Bit().data());
    return true;
}

void MarketDataEngine::reqUserLogin() {
    if (!m_pUserApi) {
        MLOG_ERROR("market", "API not initialized");
        return;
    }

    CThostFtdcReqUserLoginField req;
    memset(&req, 0, sizeof(req));
    strncpy(req.BrokerID, m_brokerID.toStdString().c_str(), sizeof(req.BrokerID) - 1);
    strncpy(req.UserID, m_userID.toStdString().c_str(), sizeof(req.UserID) - 1);
    strncpy(req.Password, m_password.toStdString().c_str(), sizeof(req.Password) - 1);

    int result = m_pUserApi->ReqUserLogin(&req, ++m_requestID);
    if (result == 0) {
        MLOG_DEBUG("market", "Login request sent");
    } else {
        MLOG_ERROR("market", "Failed to send login request");
    }
}

bool MarketDataEngine::hasError(CThostFtdcRspInfoField *pRspInfo) {
    return pRspInfo && pRspInfo->ErrorID != 0;
}

} // namespace Market